import os
import sys

import json

from django.core.mail import send_mail
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.shortcuts import render, HttpResponseRedirect
from django.utils import timezone

# models
from login.models import UserProfile
from ddm.models import DownloadTaskDetail, DownloadTaskResult

# 下载任务
from website.settings import FILE_CACHE_PATH
from ddm.tasks import approval_mail, download_odps_table, download_oss_report, extract_odps_table_meta

# 随机 id
import uuid

import logging

logger = logging.getLogger('ddm_views')


@login_required
def task_post(request, username):
    """ 下载任务 - 表单页面.
        字段解释:
        [01] task_id     : 任务id
        [02] task_name   : 任务名称
        [03] task_type   : 任务类型 - (1) 下载 odps 表 (2) 下载 oss 征信报告
        [04] priority    : 任务等级：A, B, C, D, E ; 默认: C
        [05] project     : odps 项目空间, 默认: shilupan_strategy.
        [06] table       : odps 数据表
        [07] upload_file : 上传文件(文本类型)
        [08] save_path   : 文件缓存路径(自动生成), 允许自定义路径（权限问题造成很大问题）.
        [09] file        :
        [10] is_encrypt  : 是否加密(包含隐藏字段)
        [11] limit       : 下载条数限制, 默认: 全部(若用户未输入.)
        [12] retain      : 数据保留时长.
        [13] instruction : 数据信息描述.
        [14] purpose     : 使用目的.
        [15] remake      : 额外备注.
        [16] mail_to     : 抄送邮箱.
        [17] apply_time  : 申请时间.
    """

    # 当前时间
    now = timezone.now()
    today = timezone.now().date().strftime('%Y%m%d')

    if request.method == 'POST':
        # 提取任务信息
        task_id = uuid.uuid4()
        task_name = request.POST.get('task_name')
        task_type = request.POST.get('task_type')
        priority = request.POST.get('priority', 'C')
        project = request.POST.get('project', 'shilupan_strategy')

        # 防止用户错误输入 shilupan_startegy.tablename
        table = request.POST.get('table')
        if '.' in table:
            table = table.split('.')[-1]

        # [1] 上传文件(上传订单文件下载): 功能未开发.
        upload_file = request.POST.get('upload_file', '')

        # [2] 保存路径(自定义上传文件路径): 功能未开发
        save_path = request.POST.get('save_path', '')

        # [3] 文件类型: 文件自动命名; 自动生成下载文件(报告、数据表)存储路径及文件(命名规则: /mnt/table/user/project/)
        file_type = request.POST.get('file_type')

        # 根据 username 查找用户 user 所在部门.
        user = UserProfile.objects.get(username=username)
        department = user.department

        if task_type.lower() == 'table':
            # [1] 下载 table: 需要指定下载文件的绝对路径.(同时文本分隔符: delimiter)
            delimiter = request.POST.get('delimiter')
            filename = '.'.join([''.join([now.strftime('%Y%m%d%H%M%S'), table]), file_type])
            if not save_path:
                save_path = os.path.join(FILE_CACHE_PATH, department, username, task_type, today, project, filename)
        elif task_type.lower() == 'report':
            # [2] 下载 report: 需要指定绝对路径的目录(无需指定文本分隔符: delimiter).
            delimiter = ''
            if not save_path:
                save_path = os.path.join(FILE_CACHE_PATH, department, username, task_type, today, project, table)
        else:
            delimiter = ''
            if not save_path:
                save_path = os.path.join(FILE_CACHE_PATH, department, username, task_type, project)

        is_encrypt = request.POST.get('is_encrypt')
        limit = request.POST.get('limit')
        if not limit:
            limit = 'all'
        elif isinstance(limit, int):
            limit = str(limit)
        else:
            pass
        retain = request.POST.get('retain', '12')
        instruction = request.POST.get('instruction', '未提交数据描述')
        purpose = request.POST.get('purpose', '')
        remark = request.POST.get('remark', '')
        mail_to_list = request.POST.getlist('mail_to', ['etl-test@slooptech.com', 'zhoujin@slooptech.com'])
        mail_to = ','.join(mail_to_list)

        # 默认系统当前时间
        apply_time = request.POST.get('apply_time', now)

        try:
            # [1] 下载任务详情表 TaskInfo
            DownloadTaskDetail.objects.update_or_create(task_id=task_id, task_name=task_name, apply_time=apply_time,
                                                        task_type=task_type, priority=priority, project=project,
                                                        table=table, upload_file=upload_file, save_path=save_path,
                                                        file_type=file_type, delimiter=delimiter, is_encrypt=is_encrypt,
                                                        limit=limit, retain=retain, instruction=instruction,
                                                        purpose=purpose, remark=remark, mail_to=mail_to)
            # [2] 下载任务状态表 DownloadTaskResult
            DownloadTaskResult.objects.update_or_create(task_id=task_id, username=username, apply_time=apply_time,
                                                        task_name=task_name)

            # [3] 通过异步任务方式， 更新数据表信息
            # extract_odps_table_meta.delay(task_id)
            program_exec_info = extract_odps_table_meta(task_id)
        except Exception:
            # 处理异常
            exec_info = sys.exc_info()
            logger.error('[%s] 申请人提交任务 %s 写入数据库失败, 具体原因: %s, %s, %s' % (
                apply_time.strftime('%Y-%m-%d %H:%M:%S'), task_id, exec_info[0], exec_info[1], exec_info[2]))
        else:
            # 记录用户提交任务
            logger.info('[%s] 申请人 %s 提交任务 %s, TaskID: %s.' % (apply_time.strftime('%Y-%m-%d %H:%M:%S'), username,
                                                              task_name, task_id))
            # 自动审批规则: 自动过滤某些表用于减少用户工作量.
            query_task = json.loads(program_exec_info)
            table_meta = json.loads(query_task.get('meta'))
            records = int(table_meta.get('RecordNumber', 1000))
            if (task_type.lower() == 'report') and is_encrypt.lower() == 'no' and records >= 1000:
                # 若下载明文的征信报告, 且 下载条数 >= 1000 条
                message = "下载账户: %s\n任务ID : %s\n任务名称: %s\n下载数目: %s" % (request.user.username,
                                                                       str(task_id), str(task_name), str(records))
                mail_return = send_mail('审批系统下载明文征信报告大于1000条报警提示', message,
                                        'etl-test@slooptech.com',
                                        'zhangpeng@slooptech.com')
        finally:
            # 返回用户 7 天之内的任务信息
            return HttpResponseRedirect(reverse('ddm:result', args=(username, 7)))

    return render(request, 'ddm/task.html', {'username': username})


@login_required
def task_result(request, username, days):
    """ 显示某用户 username 最近7天所有的下载任务.
    :param request : HttpRequest 请求对象.
    :param username: 用户名称.
    :param days    ：N 天之前.
    """

    # 数据开始日期
    last_7_datetime = timezone.now().date() - timezone.timedelta(days=int(days))

    # 用户对象
    user_info = UserProfile.objects.get(username=username)

    # 根据用户权限展示用户任务列表
    if user_info.is_admin:
        # 管理员: 展示 7 天之内任务列表[修改方案: 未审批任务的]
        task_list = DownloadTaskResult.objects.filter(update_time__gt=last_7_datetime).values(
                'task_id', 'task_name', 'username', 'apply_time', 'approver', 'approval', 'reason', 'status',
                'success_rate', 'receipt_mail').order_by('-apply_time')
    else:
        # 申请者: 展示 7 天 之内的任务列表
        task_list = DownloadTaskResult.objects.filter(username=username).filter(update_time__gt=last_7_datetime).values(
                'task_id', 'task_name', 'username', 'apply_time', 'approver', 'approval', 'reason', 'status',
                'success_rate', 'receipt_mail').order_by('-apply_time')

    # 分页功能实现(每页展示12行)
    paginator = Paginator(task_list, 12)

    # 获取当前 url 后的 page 页面参数
    page = request.GET.get('page')

    try:
        tasks = paginator.page(page)
    except PageNotAnInteger:
        # 返回第一页
        tasks = paginator.page(1)
    except EmptyPage:
        # 最后一页
        tasks = paginator.page(paginator.num_pages)
    finally:
        pass

    context = {
        'tasks': tasks,
        'username': username,
    }

    return render(request, 'ddm/result.html', context=context)


@login_required
def task_detail(request, username, task_id):
    """ 根据用户输入 task_id 展示任务详情页面. """
    # 查看用户权限: 是否是管理员 is_admin
    user_info = UserProfile.objects.filter(username=username).values()
    is_admin = (user_info[0]).get('is_admin', False)

    # 任务详情
    detail_list = DownloadTaskDetail.objects.filter(task_id=task_id).values()
    detail = detail_list[0]

    # 从 detail 中 提取 odps Meta 信息
    meta_info_dict = json.loads(detail.get('table_meta'))

    if meta_info_dict.get('is_success'):
        # 若成功获取 Meat, 提取表内数据
        table_meta_dict = json.loads(meta_info_dict.get('meta'))
        meta_info_dict.update(table_meta_dict)

    # 任务审批状态
    result_list = DownloadTaskResult.objects.filter(task_id=task_id).values('approval')
    approval_result = (result_list[0]).get('approval', 'Waiting')

    # 追加用户信息、任务审批状态、
    detail.update({'username': username, 'is_admin': is_admin, 'approval_result': approval_result})
    detail.update(meta_info_dict)

    return render(request, 'ddm/detail.html', context=detail)


@login_required
def task_delete(request, username, task_id):
    """ 根据 task_id 删除任务 ."""

    # 从 DownloadTaskResult 结果中删除 task_id 任务
    DownloadTaskResult.objects.filter(task_id=task_id).delete()

    # 从 DownloadTaskDetail中删除 task_id 任务
    DownloadTaskDetail.objects.filter(task_id=task_id).delete()

    # 返回到任务列表页面
    return render(request, 'ddm/result.html', context={'username': username})


@login_required
def task_approval(request, username, task_id):
    """  任务审批 """

    if request.method == 'POST':
        # 审批提交表单数据
        approval = request.POST.get('task_approval', 'Approve')
        reason = request.POST.get('approval_reason', 'System Default Approve')

        if approval.lower() == 'approve':
            # [2.1] 若审批通过, 则更新DownloadTaskResult中的任务状态
            DownloadTaskResult.objects.filter(task_id=task_id).update(approver=username, approval=approval,
                                                                      reason=reason, status='Execute',
                                                                      success_rate='0.0%', receipt_mail=False)
            # [2.2] 执行下载任务: 异步方式
            task = DownloadTaskResult.objects.get(task_id=task_id)
            applicant = task.username
            task_info = DownloadTaskDetail.objects.get(task_id=task_id)
            project = task_info.project
            table = task_info.table
            cache_path = task_info.save_path
            is_encrypt = task_info.is_encrypt
            if is_encrypt == 'NO':
                is_encrypt = False
            else:
                # 默认: MD5
                is_encrypt = True

            if task_info.limit == 'all':
                limit = None
            else:
                limit = task_info.limit

            # 设置任务过期时间
            task_expires_seconds = 60 * 60 * 24

            if task_info.task_type == 'report':
                # [3.1]下载征信报告
                yesterday = (timezone.now().date() - timezone.timedelta(days=1)).strftime('%Y%m%d')
                async_download_result = download_oss_report.apply_async(kwargs={'username': applicant,
                                                                                'project': project,
                                                                                'table': table,
                                                                                'partition': yesterday,
                                                                                'limit': limit,
                                                                                'is_encrypt': is_encrypt,
                                                                                'cache_path': cache_path},
                                                                        expires=task_expires_seconds)
            else:
                # [3.2]下载数据表
                delimiter = task_info.delimiter
                async_download_result = download_odps_table.apply_async(kwargs={'username': applicant,
                                                                                'project': project,
                                                                                'table': table,
                                                                                'partition': None,
                                                                                'limit': limit,
                                                                                'columns': None,
                                                                                'is_encrypt': is_encrypt,
                                                                                'cache_file': cache_path,
                                                                                'delimiter': delimiter},
                                                                        expires=task_expires_seconds)

            while True:
                if async_download_result.task_id is not None:
                    # 更新 DownloadTaskResult中 celery_task_id
                    DownloadTaskResult.objects.filter(task_id=task_id
                                                      ).update(celery_task_id=async_download_result.task_id)

                    break

        else:
            # [2.2] 若审批拒绝, 则更新DownloadTaskResult中的任务状态, 然后发送邮件通知申请人.
            DownloadTaskResult.objects.filter(task_id=task_id).update(approver=username, approval=approval,
                                                                      reason=reason, status='Failure',
                                                                      success_rate='0.0%', receipt_mail=False)
            # 邮件通知申请人
            celery_mail_task = approval_mail.delay(task_id, approval, reason)

    return HttpResponseRedirect(reverse('ddm:result', args=(username, 7)))
